/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ui;

import control.MStockMidlet;
import font.MyFont;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import listener.Command;
import listener.ICompleteListener;
import util.SoftKey;

/**
 *
 * @author tho.hoang
 */
public class NumberFieldEditor extends Canvas implements Runnable {

    private StringBuffer text = new StringBuffer();
    private int preNumIndex = 0, numIndex = 0, caseIndex = 0, count = -1, acc = 0, offset = 0, upperOffset = 1, countSign = -1, indexSignedChar = -1;
    private static char[] num1 = {'.', ',', '1', '*', '@', '?', '!', '_', '-', '(', ')', '\\', '~', '"', '\'', '/', '&', '#', ':', ';', '%', '=', '+', '<', '>', '[', ']', '^', '`', '{', '|', '}'};
    private static char[][][] nums = new char[][][]{{
            {'0'},
            num1,
            {'a', 'â', 'ă', 'b', 'c', '2'},
            {'d', 'e', 'ê', 'f', '3'},
            {'g', 'h', 'i', '4'},
            {'j', 'k', 'l', '5'},
            {'m', 'n', 'o', 'ô', 'ơ', '6'},
            {'p', 'q', 'r', 's', '7'},
            {'t', 'u', 'ư', 'v', '8'},
            {'w', 'x', 'y', 'z', '9'},
            {' '}, // star
        }, {
            {'0'},
            num1,
            {'A', 'Â', 'Ă', 'B', 'C', '2'},
            {'D', 'E', 'Ê', 'F', '3'},
            {'G', 'H', 'I', '4'},
            {'J', 'K', 'L', '5'},
            {'M', 'N', 'O', 'Ô', 'Ơ', '6'},
            {'P', 'Q', 'R', 'S', '7'},
            {'T', 'U', 'Ư', 'V', '8'},
            {'W', 'X', 'Y', 'Z', '9'},
            {' '}, // star
        }
    };
    private static char[] numChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '};
    boolean motorola = false, isSignedChar = false;
    final int KEY_SOFT_LEFT = -6, KEY_SOFT_RIGHT = -7, KEY_UP = -1, KEY_LEFT = -3, KEY_DOWN = -2, KEY_RIGHT = -4, KEY_FIRE = -5;
    private boolean newKey = false, showCurcos = true;
    private int margin = 2;
    private boolean showCharBack = false;
    private char curChar;
    private final int MODE_NUMBER = 0, MODE_abc = 1, MODE_Abc = 2, MODE_ABC = 3, MODE_SYMBLE = 4;
    private String[] modeString = {"123", "abc", "Abc", "ABC"};
    private int typeMode = MODE_abc;
    private boolean upperCaseFirstChar = false;
    private MyFont font;
    private int maxLength;
    private String label;
    private Style headerStyle, footerStyle, textBoxStyle;
    private Display display;
    private ICompleteListener listener;
    private Command selectCommand, cancelCommand;
    private Thread th;
    private int subIndex = 0, curcorX = 0, viewTextWidth, lastIndex;

    public NumberFieldEditor(Display display, MStockMidlet control, String label, String text, int maxLength) {
        setFullScreenMode(true);
        this.display = display;
        this.font = control.getFont();
        this.footerStyle = control.getStyle().getFooterStyle();
        this.headerStyle = control.getStyle().getHeaderStyle();
        this.textBoxStyle = control.getStyle().getTextBoxStyle();
        this.cancelCommand = new Command("Xóa", Command.BACK);
        this.selectCommand = new Command("Đồng ý", Command.OK);
        this.maxLength = maxLength;
        this.label = label;
        this.viewTextWidth = getWidth() - 2 * margin - textBoxStyle.getPadding().getLeftAndRight();
        typeMode = MODE_NUMBER;
        if (text != null) {
            this.text = new StringBuffer(text);
            this.offset = text.length();
            this.curcorX = font.getFontNormal().stringWidth(text.toString());
            this.lastIndex = text.length();
            calculateSubIndex();
        }
        this.th = new Thread(this);
        this.th.start();
        initGui();
    }

    public void show() {
        this.display.setCurrent(this);
    }

    public void setListener(ICompleteListener listener) {
        this.listener = listener;
    }

    public int getMaxLength() {
        return maxLength;
    }

    public void setMaxLength(int maxLength) {
        this.maxLength = maxLength;
    }

    public String getText() {
        return text.toString();
    }

    public void setText(String text) {
        if (text != null) {
            this.text = new StringBuffer(text);
            initGui();
        }
    }

    protected void keyReleased(int keyCode) {
        keyCode = changeKeyCode(keyCode);
    }

    protected void keyRepeated(int keyCode) {
        keyCode = changeKeyCode(keyCode);
        processKeyRepeated(keyCode);
    }

    protected void keyPressed(int keyCode) {
        keyCode = changeKeyCode(keyCode);
        if (keyCode == KEY_SOFT_LEFT) {
            if (listener != null) {
                listener.completedAction(this, text.toString());
                try {
                    th.interrupt();
                } catch (Exception e) {
                }
                th = null;
            }
        } else if (keyCode == KEY_SOFT_RIGHT) {
            if (text.length() == 0) {
                if (listener != null) {
                    listener.completedAction(this, "");
                    try {
                        th.interrupt();
                    } catch (Exception e) {
                    }
                    th = null;
                }
            } else {
                resetSignedChar();
                /* delete char */
                deleteChar();
                /* reset count */
                count = -1;
                newKey = true;
                /* reset background char */
                showCharBack = false;
                repaint();
            }
        } else {
            processKeyPress(keyCode);
            repaint();
        }
    }

    protected void paint(Graphics g) {
        setFullScreenMode(true);
        initGui();
        int height = headerStyle.getHeight() + textBoxStyle.getHeight() + footerStyle.getHeight();
        int x = 0, y = getHeight() - height, headerH = 0;

        // bg header
        g.setColor(headerStyle.getBgColor());
        g.fillRect(x, y, getWidth(), headerStyle.getHeight());
        // label
        if (label != null && label.length() > 0) {
            headerH = font.getFontBold().getHeight() + headerStyle.getRowHeight();
            font.getFontBold().drawString(g, label, getWidth() / 2, y + headerStyle.getPadding().getTop(), Graphics.TOP | Graphics.HCENTER);
        }
        // limited text
        font.getFontNormal().drawString(g, text.length() + "/" + maxLength, x + headerStyle.getPadding().getLeft(), y + headerStyle.getPadding().getTop() + headerH, Graphics.TOP | Graphics.LEFT);
        // type mode
        font.getFontNormal().drawString(g, modeString[typeMode], getWidth() - headerStyle.getPadding().getRight(), y + headerStyle.getPadding().getTop() + headerH, Graphics.TOP | Graphics.RIGHT);

        y += headerStyle.getHeight();
        // bg text box
        g.setColor(textBoxStyle.getSelectedBgColor());
        g.fillRect(x, y, getWidth(), textBoxStyle.getHeight());
        // box
        g.setColor(textBoxStyle.getSelectedBorderColor());
        g.drawRect(x, y, getWidth() - 1, textBoxStyle.getHeight() - 1);
        if (showCharBack && text.length() != 0) {
            g.setColor(0xdddddd);
            int vx = font.getFontNormal().substringWidth(text.toString(), subIndex, offset - subIndex - 1) + 1 + textBoxStyle.getPadding().getLeft();
            int w = font.getFontNormal().charWidth(text.charAt(offset - 1));
            g.fillRect(vx, y + textBoxStyle.getPadding().getTop(), w, font.getFontNormal().getHeight());
            g.setColor(textBoxStyle.getSelectedBorderColor());
        }
        /* draw text */
        font.getFontNormal().drawSubstring(g, text.toString(), subIndex, lastIndex, x + textBoxStyle.getPadding().getLeft(), y + textBoxStyle.getPadding().getTop(), Graphics.TOP | Graphics.LEFT);
        /* draw cursor */
        if (showCurcos) {
            g.drawLine(curcorX + margin, y + textBoxStyle.getPadding().getTop(), curcorX + margin, y + textBoxStyle.getPadding().getTop() + font.getFontNormal().getHeight());
        }

        y += textBoxStyle.getHeight();
        // bg footer
        g.setColor(footerStyle.getBgColor());
        g.fillRect(x, y, getWidth(), footerStyle.getHeight());
        // left command
        if (selectCommand != null) {
            font.getFontBold().drawString(g, selectCommand.getText(), x + footerStyle.getPadding().getLeft(), y + footerStyle.getPadding().getTop(), Graphics.TOP | Graphics.LEFT);
        }
        // right command
        if (cancelCommand != null) {
            if (text.length() != 0) {
                font.getFontBold().drawString(g, cancelCommand.getText(), getWidth() - footerStyle.getPadding().getRight(), y + footerStyle.getPadding().getTop(), Graphics.TOP | Graphics.RIGHT);
            } else {
                font.getFontBold().drawString(g, "Trở về", getWidth() - footerStyle.getPadding().getRight(), y + footerStyle.getPadding().getTop(), Graphics.TOP | Graphics.RIGHT);
            }
        }

        System.gc();
    }

    private void initGui() {
        // init header
        int headerH = 0;
        if (label != null && label.length() > 0) {
            headerH = font.getFontBold().getHeight();
        }
        headerH += font.getFontNormal().getHeight() + headerStyle.getRowHeight();
        headerStyle.setHeight(headerH + headerStyle.getPadding().getTopAndBottom());

        // init Text Box
        textBoxStyle.setHeight(font.getFontNormal().getHeight() + textBoxStyle.getPadding().getTopAndBottom() + 2);

        // init footer
        if (selectCommand != null || cancelCommand != null) {
            footerStyle.setHeight(font.getFontBold().getHeight() + footerStyle.getPadding().getTopAndBottom());
        } else {
            footerStyle.setHeight(0);
        }
    }

    private int changeKeyCode(int keyCode) {
        int key = keyCode;
        if (keyCode == SoftKey.getLeftSoftkeyCode(this)) {
            key = KEY_SOFT_LEFT;

        } else if (keyCode == SoftKey.getRightSoftkeyCode(this)) {
            key = KEY_SOFT_RIGHT;

        } else {
            switch (keyCode) {
                case KEY_NUM0:
                case KEY_NUM1:
                case KEY_NUM2:
                case KEY_NUM3:
                case KEY_NUM4:
                case KEY_NUM5:
                case KEY_NUM6:
                case KEY_NUM7:
                case KEY_NUM8:
                case KEY_NUM9:
                case KEY_STAR:
                case KEY_POUND:
                case FIRE:
                    key = keyCode;
                    break;
                default:
                    int gameAction = getGameAction(keyCode);
                    switch (gameAction) {
                        case UP:
                            key = KEY_UP;
                            break;
                        case DOWN:
                            key = KEY_DOWN;
                            break;
                        case LEFT:
                            key = KEY_LEFT;
                            break;
                        case RIGHT:
                            key = KEY_RIGHT;
                            break;
                    }
                    break;
            }
        }
        return key;
    }

    private void deleteChar() {
        /* delete char */
        if (offset != 0) {
            /* reduce the obscissa of curcor */
            curcorX -= font.getFontNormal().charWidth(text.charAt(offset - 1));
            text.delete(offset - 1, offset);
            offset--;

            calculateSubIndex();
        }
    }

    private void processKeyPress(int keyCode) {
        preNumIndex = numIndex;
        /* check if pressing from number 1 to 9 */
        if (keyCode >= KEY_NUM0 && keyCode <= KEY_NUM9) {
            numIndex = keyCode - KEY_NUM0;
            insertChar();
        } else if (keyCode == KEY_STAR) {
        } else if (keyCode == KEY_UP) {
        } else if (keyCode == KEY_DOWN) {
        } else if (keyCode == KEY_LEFT) {
            resetSignedChar();
            /* move back cursor with a char */
            if (offset > 0) {
                offset--;
                /* reduce the obscissa of curcor */
                curcorX -= font.getFontNormal().charWidth(text.charAt(offset));
                /* reset number index */
                numIndex = -1;
                /* reset background char */
                showCharBack = false;

                calculateSubIndex();
            }
        } else if (keyCode == KEY_RIGHT) {
            resetSignedChar();
            /* move over cursor with a char */
            if (offset < text.length()) {
                /* increase the obscissa of curcor */
                curcorX += font.getFontNormal().charWidth(text.charAt(offset));
                offset++;
            }
            /* reset number index */
            numIndex = -1;
            /* reset background char */
            showCharBack = false;

            calculateSubIndex();
        }
    }

    private void insertChar() {
        resetSignedChar();
        /* view background char */
        showCharBack = true;
        /* reset counting number to hide background char */
        acc = 1;
        /* check if press same key */
        if (preNumIndex == numIndex && typeMode != MODE_NUMBER) {
            /* change index char of key */
            count++;
            newKey = false;
        } else {
            /* if press other key */
            count = 0;
            /* change offset */
            newKey = true;
        }
        if (!newKey) {
            deleteChar();
        }
        if (upperCaseFirstChar && offset == upperOffset) {
            resetMode();
        }
        /* check max size */
        if (this.text.length() >= this.maxLength) {
            return;
        }
        text.insert(offset, getInsertedChar());
        /* increase the obscissa of curcor */
        curcorX += font.getFontNormal().charWidth(text.charAt(offset));
        offset++;

        calculateSubIndex();
    }

    private char getInsertedChar() {
        /** get index of number key */
        int charIndex = count % nums[caseIndex][numIndex].length;
        /** get current char */
        if (typeMode == MODE_NUMBER) {
            curChar = numChars[numIndex];
        } else {
            curChar = nums[caseIndex][numIndex][charIndex];
        }
        return curChar;
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(300);
                acc++;
                if (acc % 5 == 0) {
                    numIndex = -1;
                    showCharBack = false;
                    resetSignedChar();
                    if (upperCaseFirstChar && offset == upperOffset) {
                        resetMode();
                    }
                }
                repaint();
                showCurcos = !showCurcos;

            } catch (InterruptedException ex) {
//                ex.printStackTrace();
            }
        }
    }

    private void processKeyRepeated(int keyCode) {
        processKeyPress(keyCode);
    }

    private void resetSignedChar() {
        /* reset signed char */
        indexSignedChar = -1;
        isSignedChar = false;
        curChar = 'z';
        countSign = -1;
    }

    private void resetMode() {
        if (typeMode == MODE_Abc) {
            typeMode = MODE_abc;
            caseIndex = 0;
            upperCaseFirstChar = false;
            upperOffset = 1;
        }
    }

    /* check and calculate subindex to be able showing the text with curcor is viewed */
    private void calculateSubIndex() {
        if (font.getFontNormal().stringWidth(text.toString()) <= viewTextWidth) {
            this.lastIndex = text.length();
            if (this.curcorX <= 0) {
                subIndex = 0;
                // recalculate the abscissa of curcor
                this.curcorX = font.getFontNormal().substringWidth(text.toString(), this.subIndex, offset - this.subIndex);
            }
            return;
        }
        if (this.curcorX > viewTextWidth) {
            this.lastIndex = Math.min(lastIndex + 2, text.length());
            int tem = lastIndex - 1;
            while (font.getFontNormal().substringWidth(text.toString(), tem, lastIndex - tem) < viewTextWidth) {
                tem--;
            }
            if (this.lastIndex == text.length()) {
                this.subIndex = tem + 2;
            } else {
                this.subIndex = tem;
            }
            // recalculate the abscissa of curcor
            this.curcorX = font.getFontNormal().substringWidth(text.toString(), this.subIndex, offset - this.subIndex);
        } else if (this.curcorX < 0) {
            subIndex = Math.max(subIndex - 2, 0);
            int tem = subIndex + 1;
            while (font.getFontNormal().substringWidth(text.toString(), subIndex, tem - subIndex) < viewTextWidth) {
                tem++;
            }
            this.lastIndex = tem;
            // recalculate the abscissa of curcor
            this.curcorX = font.getFontNormal().substringWidth(text.toString(), this.subIndex, offset - this.subIndex);
        } else {
            int tem = subIndex + 1;
            while (font.getFontNormal().substringWidth(text.toString(), subIndex, tem - subIndex) < viewTextWidth && tem < text.length()) {
                tem++;
            }
            this.lastIndex = tem;
        }
    }
}
